Trò chơi Tic-Tac-Toe, game đánh caro full source code
- ThirdPersonController.cs
- Player /
- Game /
- Scripts /
- DemoWorker /
- Demos /
- Photon Unity Networking /
- Assets /
- project /
2 using System.Collections;
3
4 public enum CharacterState
5 {
6 Idle = 0,
7 Walking = 1,
8 Trotting = 2,
9 Running = 3,
10 Jumping = 4,
11 }
12
13 public class ThirdPersonController : MonoBehaviour
14 {
15
16 public AnimationClip idleAnimation;
17 public AnimationClip walkAnimation;
18 public AnimationClip runAnimation;
19 public AnimationClip jumpPoseAnimation;
20
21 public float walkMaxAnimationSpeed = 0.75f;
22 public float trotMaxAnimationSpeed = 1.0f;
23 public float runMaxAnimationSpeed = 1.0f;
24 public float jumpAnimationSpeed = 1.15f;
25 public float landAnimationSpeed = 1.0f;
26
27 private Animation _animation;
28
29
30
31 public CharacterState _characterState;
32
33 // The speed when walking
34 public float walkSpeed = 2.0f;
35 // after trotAfterSeconds of walking we trot with trotSpeed
36 public float trotSpeed = 4.0f;
37 // when pressing "Fire3" button (cmd) we start running
38 public float runSpeed = 6.0f;
39
40 public float inAirControlAcceleration = 3.0f;
41
42 // How high do we jump when pressing jump and letting go immediately
43 public float jumpHeight = 0.5f;
44
45 // The gravity for the character
46 public float gravity = 20.0f;
47 // The gravity in controlled descent mode
48 public float speedSmoothing = 10.0f;
49 public float rotateSpeed = 500.0f;
50 public float trotAfterSeconds = 3.0f;
51
52 public bool canJump = false;
53
54 private float jumpRepeatTime = 0.05f;
55 private float jumpTimeout = 0.15f;
56 private float groundedTimeout = 0.25f;
57
58 // The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
59 private float lockCameraTimer = 0.0f;
60
61 // The current move direction in x-z
62 private Vector3 moveDirection = Vector3.zero;
63 // The current vertical speed
64 private float verticalSpeed = 0.0f;
65 // The current x-z move speed
66 private float moveSpeed = 0.0f;
67
68 // The last collision flags returned from controller.Move
69 private CollisionFlags collisionFlags;
70
71 // Are we jumping? (Initiated with jump button and not grounded yet)
72 private bool jumping = false;
73 private bool jumpingReachedApex = false;
74
75 // Are we moving backwards (This locks the camera to not do a 180 degree spin)
76 private bool movingBack = false;
77 // Is the user pressing any keys?
78 private bool isMoving = false;
79 // When did the user start walking (Used for going into trot after a while)
80 private float walkTimeStart = 0.0f;
81 // Last time the jump button was clicked down
82 private float lastJumpButtonTime = -10.0f;
83 // Last time we performed a jump
84 private float lastJumpTime = -1.0f;
85 // the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
86 //private float lastJumpStartHeight = 0.0f;
87 private Vector3 inAirVelocity = Vector3.zero;
88
89 private float lastGroundedTime = 0.0f;
90 public bool isControllable = true;
91
92 void Awake()
93 {
94 moveDirection = transform.TransformDirection(Vector3.forward);
95
96 _animation = GetComponent<Animation>();
97 if (!_animation)
98 Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird.");
99
100 /*
101 public AnimationClip idleAnimation;
102 public AnimationClip walkAnimation;
103 public AnimationClip runAnimation;
104 public AnimationClip jumpPoseAnimation;
105 */
106 if (!idleAnimation)
107 {
108 _animation = null;
109 Debug.Log("No idle animation found. Turning off animations.");
110 }
111 if (!walkAnimation)
112 {
113 _animation = null;
114 Debug.Log("No walk animation found. Turning off animations.");
115 }
116 if (!runAnimation)
117 {
118 _animation = null;
119 Debug.Log("No run animation found. Turning off animations.");
120 }
121 if (!jumpPoseAnimation && canJump)
122 {
123 _animation = null;
124 Debug.Log("No jump animation found and the character has canJump enabled. Turning off animations.");
125 }
126
127 }
128
129 private Vector3 lastPos;
130
131 void UpdateSmoothedMovementDirection()
132 {
133 Transform cameraTransform = Camera.main.transform;
134 bool grounded = IsGrounded();
135
136 // Forward vector relative to the camera along the x-z plane
137 Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);
138 forward.y = 0;
139 forward = forward.normalized;
140
141 // Right vector relative to the camera
142 // Always orthogonal to the forward vector
143 Vector3 right = new Vector3(forward.z, 0, -forward.x);
144
145 float v = Input.GetAxisRaw("Vertical");
146 float h = Input.GetAxisRaw("Horizontal");
147
148 // Are we moving backwards or looking backwards
149 if (v < -0.2f)
150 movingBack = true;
151 else
152 movingBack = false;
153
154 bool wasMoving = isMoving;
155 isMoving = Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f;
156
157 // Target direction relative to the camera
158 Vector3 targetDirection = h * right + v * forward;
159
160 // Grounded controls
161 if (grounded)
162 {
163 // Lock camera for short period when transitioning moving & standing still
164 lockCameraTimer += Time.deltaTime;
165 if (isMoving != wasMoving)
166 lockCameraTimer = 0.0f;
167
168 // We store speed and direction seperately,
169 // so that when the character stands still we still have a valid forward direction
170 // moveDirection is always normalized, and we only update it if there is user input.
171 if (targetDirection != Vector3.zero)
172 {
173 // If we are really slow, just snap to the target direction
174 if (moveSpeed < walkSpeed * 0.9f && grounded)
175 {
176 moveDirection = targetDirection.normalized;
177 }
178 // Otherwise smoothly turn towards it
179 else
180 {
181 moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
182
183 moveDirection = moveDirection.normalized;
184 }
185 }
186
187 // Smooth the speed based on the current target direction
188 float curSmooth = speedSmoothing * Time.deltaTime;
189
190 // Choose target speed
191 //* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
192 float targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);
193
194 _characterState = CharacterState.Idle;
195
196 // Pick speed modifier
197 if (Input.GetKey(KeyCode.LeftShift) | Input.GetKey(KeyCode.RightShift))
198 {
199 targetSpeed *= runSpeed;
200 _characterState = CharacterState.Running;
201 }
202 else if (Time.time - trotAfterSeconds > walkTimeStart)
203 {
204 targetSpeed *= trotSpeed;
205 _characterState = CharacterState.Trotting;
206 }
207 else
208 {
209 targetSpeed *= walkSpeed;
210 _characterState = CharacterState.Walking;
211 }
212
213 moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
214
215 // Reset walk time start when we slow down
216 if (moveSpeed < walkSpeed * 0.3f)
217 walkTimeStart = Time.time;
218 }
219 // In air controls
220 else
221 {
222 // Lock camera while in air
223 if (jumping)
224 lockCameraTimer = 0.0f;
225
226 if (isMoving)
227 inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
228 }
229
230
231
232 }
233 void ApplyJumping()
234 {
235 // Prevent jumping too fast after each other
236 if (lastJumpTime + jumpRepeatTime > Time.time)
237 return;
238
239 if (IsGrounded())
240 {
241 // Jump
242 // - Only when pressing the button down
243 // - With a timeout so you can press the button slightly before landing
244 if (canJump && Time.time < lastJumpButtonTime + jumpTimeout)
245 {
246 verticalSpeed = CalculateJumpVerticalSpeed(jumpHeight);
247 SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
248 }
249 }
250 }
251 void ApplyGravity()
252 {
253 if (isControllable) // don't move player at all if not controllable.
254 {
255 // Apply gravity
256 //bool jumpButton = Input.GetButton("Jump");
257
258 // When we reach the apex of the jump we send out a message
259 if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0f)
260 {
261 jumpingReachedApex = true;
262 SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);
263 }
264
265 if (IsGrounded())
266 verticalSpeed = 0.0f;
267 else
268 verticalSpeed -= gravity * Time.deltaTime;
269 }
270 }
271
272 float CalculateJumpVerticalSpeed(float targetJumpHeight)
273 {
274 // From the jump height and gravity we deduce the upwards speed
275 // for the character to reach at the apex.
276 return Mathf.Sqrt(2 * targetJumpHeight * gravity);
277 }
278
279 void DidJump()
280 {
281 jumping = true;
282 jumpingReachedApex = false;
283 lastJumpTime = Time.time;
284 //lastJumpStartHeight = transform.position.y;
285 lastJumpButtonTime = -10;
286
287 _characterState = CharacterState.Jumping;
288 }
289
290 Vector3 velocity = Vector3.zero;
291
292 void Update()
293 {
294 if (isControllable)
295 {
296 if (Input.GetButtonDown("Jump"))
297 {
298 lastJumpButtonTime = Time.time;
299 }
300
301 UpdateSmoothedMovementDirection();
302
303 // Apply gravity
304 // - extra power jump modifies gravity
305 // - controlledDescent mode modifies gravity
306 ApplyGravity();
307
308 // Apply jumping logic
309 ApplyJumping();
310
311
312 // Calculate actual motion
313 Vector3 movement = moveDirection * moveSpeed + new Vector3(0, verticalSpeed, 0) + inAirVelocity;
314 movement *= Time.deltaTime;
315
316 // Move the controller
317 CharacterController controller = GetComponent<CharacterController>();
318 collisionFlags = controller.Move(movement);
319 }
320 velocity = (transform.position - lastPos)*25;
321
322 // ANIMATION sector
323 if (_animation)
324 {
325 if (_characterState == CharacterState.Jumping)
326 {
327 if (!jumpingReachedApex)
328 {
329 _animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed;
330 _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
331 _animation.CrossFade(jumpPoseAnimation.name);
332 }
333 else
334 {
335 _animation[jumpPoseAnimation.name].speed = -landAnimationSpeed;
336 _animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
337 _animation.CrossFade(jumpPoseAnimation.name);
338 }
339 }
340 else
341 {
342 if (this.isControllable && velocity.sqrMagnitude < 0.001f)
343 {
344 _characterState = CharacterState.Idle;
345 _animation.CrossFade(idleAnimation.name);
346 }
347 else
348 {
349 if (_characterState == CharacterState.Idle)
350 {
351 _animation.CrossFade(idleAnimation.name);
352 }
353 else if (_characterState == CharacterState.Running)
354 {
355 _animation[runAnimation.name].speed = runMaxAnimationSpeed;
356 if (this.isControllable)
357 {
358 _animation[runAnimation.name].speed = Mathf.Clamp(velocity.magnitude, 0.0f, runMaxAnimationSpeed);
359 }
360 _animation.CrossFade(runAnimation.name);
361 }
362 else if (_characterState == CharacterState.Trotting)
363 {
364 _animation[walkAnimation.name].speed = trotMaxAnimationSpeed;
365 if (this.isControllable)
366 {
367 _animation[walkAnimation.name].speed = Mathf.Clamp(velocity.magnitude, 0.0f, trotMaxAnimationSpeed);
368 }
369 _animation.CrossFade(walkAnimation.name);
370 }
371 else if (_characterState == CharacterState.Walking)
372 {
373 _animation[walkAnimation.name].speed = walkMaxAnimationSpeed;
374 if (this.isControllable)
375 {
376 _animation[walkAnimation.name].speed = Mathf.Clamp(velocity.magnitude, 0.0f, walkMaxAnimationSpeed);
377 }
378 _animation.CrossFade(walkAnimation.name);
379 }
380
381 }
382 }
383 }
384 // ANIMATION sector
385
386 // Set rotation to the move direction
387 if (IsGrounded())
388 {
389
390 transform.rotation = Quaternion.LookRotation(moveDirection);
391
392 }
393 else
394 {
395 /* This causes choppy behaviour when colliding with SIDES
396 * Vector3 xzMove = velocity;
397 xzMove.y = 0;
398 if (xzMove.sqrMagnitude > 0.001f)
399 {
400 transform.rotation = Quaternion.LookRotation(xzMove);
401 }*/
402 }
403
404 // We are in jump mode but just became grounded
405 if (IsGrounded())
406 {
407 lastGroundedTime = Time.time;
408 inAirVelocity = Vector3.zero;
409 if (jumping)
410 {
411 jumping = false;
412 SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);
413 }
414 }
415
416 lastPos = transform.position;
417 }
418
419 void OnControllerColliderHit(ControllerColliderHit hit)
420 {
421 // Debug.DrawRay(hit.point, hit.normal);
422 if (hit.moveDirection.y > 0.01f)
423 return;
424 }
425
426 public float GetSpeed()
427 {
428 return moveSpeed;
429 }
430
431 public bool IsJumping()
432 {
433 return jumping;
434 }
435
436 public bool IsGrounded()
437 {
438 return (collisionFlags & CollisionFlags.CollidedBelow) != 0;
439 }
440
441 public Vector3 GetDirection()
442 {
443 return moveDirection;
444 }
445
446 public bool IsMovingBackwards()
447 {
448 return movingBack;
449 }
450
451 public float GetLockCameraTimer()
452 {
453 return lockCameraTimer;
454 }
455
456 public bool IsMoving()
457 {
458 return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f;
459 }
460
461 public bool HasJumpReachedApex()
462 {
463 return jumpingReachedApex;
464 }
465
466 public bool IsGroundedWithTimeout()
467 {
468 return lastGroundedTime + groundedTimeout > Time.time;
469 }
470
471 public void Reset()
472 {
473 gameObject.tag = "Player";
474 }
475
476
477 }
The speed when walking
after trotAfterSeconds of walking we trot with trotSpeed
when pressing "Fire3" button (cmd) we start running
How high do we jump when pressing jump and letting go immediately
The gravity for the character
The gravity in controlled descent mode
The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.
The current move direction in x-z
The current vertical speed
The current x-z move speed
The last collision flags returned from controller.Move
Are we jumping? (Initiated with jump button and not grounded yet)
Are we moving backwards (This locks the camera to not do a 180 degree spin)
Is the user pressing any keys?
When did the user start walking (Used for going into trot after a while)
Last time the jump button was clicked down
Last time we performed a jump
the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
private float lastJumpStartHeight = 0.0f;
Forward vector relative to the camera along the x-z plane
Right vector relative to the camera
Always orthogonal to the forward vector
Are we moving backwards or looking backwards
Target direction relative to the camera
Grounded controls
Lock camera for short period when transitioning moving & standing still
We store speed and direction seperately,
so that when the character stands still we still have a valid forward direction
moveDirection is always normalized, and we only update it if there is user input.
If we are really slow, just snap to the target direction
Otherwise smoothly turn towards it
Smooth the speed based on the current target direction
Choose target speed
* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways
Pick speed modifier
Reset walk time start when we slow down
In air controls
Lock camera while in air
Prevent jumping too fast after each other
Jump
- Only when pressing the button down
- With a timeout so you can press the button slightly before landing
if (isControllable) don't move player at all if not controllable.
Apply gravity
bool jumpButton = Input.GetButton("Jump");
When we reach the apex of the jump we send out a message
From the jump height and gravity we deduce the upwards speed
for the character to reach at the apex.
lastJumpStartHeight = transform.position.y;
Apply gravity
- extra power jump modifies gravity
- controlledDescent mode modifies gravity
Apply jumping logic
Calculate actual motion
Move the controller
ANIMATION sector
ANIMATION sector
Set rotation to the move direction
We are in jump mode but just became grounded
Debug.DrawRay(hit.point, hit.normal);